package concurrent.c_005_AtomicXXX_CAS;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * https://www.cnblogs.com/jhxxb/p/11552150.html
 * AtomicReference、AtomicStampedReference 和 AtomicMarkableReference
 * 这三个都是自 JDK1.5 开始加入到 java.util.concurrent.atomic 下面的。他们都可以在 lock-free 的情况下以原子的方式更新对象引用。
 */
public class T06_CAS_ABA {
    @Getter
    @Setter
    @AllArgsConstructor
    static class User {
        private int age;
    }

    public static void main(String[] args) {
        User user1 = new User(10);
        User user2 = new User(20);

        // ===============================================================================
        AtomicReference<User> atomicReference = new AtomicReference<>(user1);
        System.out.println(atomicReference.get().getAge());
        // 以原子方式更新对象引用
        System.out.println(atomicReference.compareAndSet(user1, user2));
        System.out.println(atomicReference.get().getAge());
        System.out.println(atomicReference.compareAndSet(user1, user2));

        // ===============================================================================
        // 解决了 AtomicReference 中 CAS 操作存在的 ABA 问题
        AtomicStampedReference<User> stampedReference1 = new AtomicStampedReference<>(user1, 1);
        System.out.println(stampedReference1.getReference().getAge());
        // 获取当前版本号
        int oldStamp1 = stampedReference1.getStamp();
        // 预期引用，新引用，预期版本号，新版本号
        System.out.println(stampedReference1.compareAndSet(user1, user2, oldStamp1, oldStamp1 + 1));
        System.out.println(stampedReference1.getReference().getAge());

        // ===============================================================================
        // 相对于 AtomicStampedReference，有时候，我们并不关心引用变量更改了几次，只是单纯的关心是否更改过
        AtomicMarkableReference<User> stampedReference2 = new AtomicMarkableReference<>(user1, false);
        System.out.println(stampedReference2.getReference().getAge());
        // 获取当前状态
        boolean oldStamp2 = stampedReference2.isMarked();
        // 预期引用，新引用，预期状态，新状态
        System.out.println(stampedReference2.compareAndSet(user1, user2, oldStamp2, !oldStamp2));
        System.out.println(stampedReference2.getReference().getAge());
    }
}
